package domain

import (
	"encoding/xml"
	"fmt"
	"time"

	log "github.com/sirupsen/logrus"
	"libvirt.org/go/libvirt"
	"libvirt.org/go/libvirtxml"

	"github.com/yourusername/lvm-manager/internal/libvirtconn"
)

// ConsoleService 专门处理虚拟机控制台相关功能
type ConsoleService struct {
	connManager *libvirtconn.ConnManager
	logger      *log.Logger
}

// NewConsoleService 创建新的控制台服务实例
func NewConsoleService(connManager *libvirtconn.ConnManager, logger *log.Logger) *ConsoleService {
	return &ConsoleService{
		connManager: connManager,
		logger:      logger,
	}
}

// ConsoleStream 控制台流信息
// 用于表示虚拟机控制台的流连接
type ConsoleStream struct {
	Stream   *libvirt.Stream // libvirt流对象
	Device   string          // 设备名称 (console0, serial0等)
	Protocol string          // 协议类型 (telnet, ssh, raw)
}

// OpenConsoleByUUID 通过UUID打开域的控制台流
func (cs *ConsoleService) OpenConsoleByUUID(uuid string, consoleType string) (*ConsoleStream, error) {
	var consoleStream *ConsoleStream

	err := cs.connManager.WithConn(func(conn *libvirt.Connect) error {
		dom, err := conn.LookupDomainByUUIDString(uuid)
		if err != nil {
			return fmt.Errorf("domain not found: %w", err)
		}
		defer func() {
			if dom != nil {
				dom.Free()
			}
		}()

		// 创建流
		stream, err := conn.NewStream(0)
		if err != nil {
			return fmt.Errorf("failed to create stream: %w", err)
		}
		defer func() {
			// 如果函数返回错误且stream尚未被接管，则释放stream
			if err != nil && stream != nil {
				stream.Free()
			}
		}()

		// 根据类型确定设备名称
		deviceName := cs.getDeviceName(dom, consoleType)

		// 打开控制台
		flags := libvirt.DOMAIN_CONSOLE_FORCE
		// 添加超时机制
		errCh := make(chan error, 1)
		go func() {
			errCh <- dom.OpenConsole(deviceName, stream, flags)
		}()

		var openErr error
		select {
		case openErr = <-errCh:
			if openErr != nil {
				return fmt.Errorf("failed to open console for device '%s': %w", deviceName, openErr)
			}
		case <-time.After(5 * time.Second):
			return fmt.Errorf("timeout while opening console for device '%s'", deviceName)
		}

		consoleStream = &ConsoleStream{
			Stream:   stream,
			Device:   deviceName,
			Protocol: "raw", // 默认协议
		}

		return nil
	})

	return consoleStream, err
}

// getDeviceName 根据控制台类型获取设备名称
func (cs *ConsoleService) getDeviceName(dom *libvirt.Domain, consoleType string) string {
	switch consoleType {
	case "console":
		return "console0"
	case "parallel":
		return "parallel0"
	case "channel":
		return "channel0"
	case "serial":
		return "serial0"
	default:
		// 尝试自动检测设备
		deviceName := cs.findAvailableConsoleDevice(dom, consoleType)
		if deviceName == "" {
			cs.logger.Debugf("No available device found for type '%s', falling back to console0", consoleType)
			return "console0" // 默认回退到console0
		}
		return deviceName
	}
}

// findAvailableConsoleDevice 查找可用的控制台设备
func (cs *ConsoleService) findAvailableConsoleDevice(dom *libvirt.Domain, preferredType string) string {
	// 获取域的XML描述
	xmlDesc, err := dom.GetXMLDesc(0)
	if err != nil {
		cs.logger.Debugf("Failed to get domain XML for console device detection: %v", err)
		return ""
	}

	// 解析XML
	var domainDef libvirtxml.Domain
	if err := xml.Unmarshal([]byte(xmlDesc), &domainDef); err != nil {
		cs.logger.Debugf("Failed to parse domain XML for console device detection: %v", err)
		return ""
	}

	// 根据首选类型查找设备
	switch preferredType {
	case "console":
		if domainDef.Devices != nil && len(domainDef.Devices.Consoles) > 0 {
			// 返回第一个控制台设备
			return fmt.Sprintf("console%d", 0)
		}
	case "serial":
		if domainDef.Devices != nil && len(domainDef.Devices.Serials) > 0 {
			// 返回第一个串行端口设备
			return fmt.Sprintf("serial%d", 0)
		}
	case "parallel":
		if domainDef.Devices != nil && len(domainDef.Devices.Parallels) > 0 {
			// 返回第一个并行端口设备
			return fmt.Sprintf("parallel%d", 0)
		}
	case "channel":
		if domainDef.Devices != nil && len(domainDef.Devices.Channels) > 0 {
			// 返回第一个通道设备
			return fmt.Sprintf("channel%d", 0)
		}
	}

	// 默认返回空字符串
	return ""
}

// CloseConsoleStream 关闭控制台流
func (cs *ConsoleService) CloseConsoleStream(consoleStream *ConsoleStream) error {
	if consoleStream == nil || consoleStream.Stream == nil {
		return nil
	}

	return consoleStream.Stream.Free()
}
